
package com.gitee.jmash.rbac.service;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import com.gitee.jmash.core.orm.DtoPage;
import com.gitee.jmash.core.orm.DtoTotal;
import com.gitee.jmash.core.orm.cdi.JpaTenantService;
import com.gitee.jmash.core.orm.jpa.TenantEntityManager;
import com.gitee.jmash.core.orm.tenant.TenantService;
import com.gitee.jmash.core.transaction.JakartaTransaction;
import com.gitee.jmash.rbac.dao.PermDao;
import com.gitee.jmash.rbac.dao.PermsDependDao;
import com.gitee.jmash.rbac.entity.PermEntity;
import jakarta.enterprise.inject.Typed;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.transaction.Transactional.TxType;
import jakarta.validation.executable.ValidateOnExecution;
import jmash.rbac.protobuf.PermReq;


/**
 * 权限表 rbac_perm读服务.
 *
 * @author <a href="mailto:service@crenjoy.com">crenjoy</a>
 */
@Typed(PermRead.class)
@Transactional(TxType.SUPPORTS)
@JpaTenantService
@ValidateOnExecution
public class PermReadBean implements PermRead, JakartaTransaction {

  protected TenantEntityManager tem = new TenantEntityManager();

  protected PermDao permDao = new PermDao(this.tem);

  protected PermsDependDao permsDependDao = new PermsDependDao(this.tem);


  @PersistenceContext(unitName = "ReadRbac")
  public void setEntityManager(EntityManager entityManager) {
    this.tem.setEntityManager(entityManager, false);
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T extends TenantService> T setTenant(String tenant) {
    this.tem.setTenant(tenant);
    return (T) this;
  }

  @Override
  public EntityManager getEntityManager() {
    return this.tem.getEntityManager();
  }

  @Override
  public void setTenantOnly(String tenant) {
    this.tem.setTenantOnly(tenant);
  }

  @Override
  public String getTenant() {
    return this.tem.getTenant();
  }

  @Override
  public PermEntity findById(UUID entityId) {
    return permDao.find(entityId);
  }

  @Override
  public DtoPage<PermEntity, DtoTotal> findPageByReq(PermReq req) {
    return permDao.findPageByReq(req);
  }

  @Override
  public List<PermEntity> findListByReq(PermReq req) {
    return permDao.findListByReq(req);
  }

  @Override
  public Set<String> findUserPerms(Set<UUID> roleIds) {
    Set<String> perms = new TreeSet<>();
    for (UUID roleId : roleIds) {
      List<String> permList = permDao.findRolePerms(roleId);
      perms.addAll(permList);
    }
    // 权限依赖
    Map<String, Set<String>> depend = permsDependDao.findMap(null);
    Set<String> newPerms = findDepend(perms, perms, depend);
    perms.addAll(newPerms);
    // 新增站点权限
    Set<String> addPerms = new HashSet<>();
    for (String perm : perms) {
      if (perm.startsWith("site:")) {
        String[] permArray = perm.split(":");
        // site:siteId:channelId:%
        permArray[permArray.length - 1] = "%";
        String newPerm = StringUtils.join(permArray, ":");
        addPerms.add(newPerm);
        // site:siteId:%
        String[] copiedArray = new String[permArray.length - 1];
        System.arraycopy(permArray, 0, copiedArray, 0, permArray.length - 2);
        copiedArray[copiedArray.length - 1] = "%";
        newPerm = StringUtils.join(copiedArray, ":");
        addPerms.add(newPerm);
      }
    }
    perms.addAll(addPerms);
    return perms;
  }

  /**
   * 找到嵌套依赖权限,避免死循环
   * 
   * @param allPerms 全部权限
   * @param perms 新权限
   * @param depend 依赖项
   * @return
   */
  public Set<String> findDepend(Set<String> allPerms, Set<String> perms,
      Map<String, Set<String>> depend) {
    Set<String> newPerms = new TreeSet<>();
    for (String perm : perms) {
      if (depend.containsKey(perm)) {
        for (String dependPerm : depend.get(perm)) {
          if (!allPerms.contains(dependPerm)) {
            newPerms.add(dependPerm);
          }
        }
      }
    }
    if (!newPerms.isEmpty()) {
      allPerms.addAll(newPerms);
      Set<String> childPerms = findDepend(allPerms, newPerms, depend);
      newPerms.addAll(childPerms);
    }
    return newPerms;
  }

  @Override
  public List<String> findRolePerms(UUID roleId) {
    return permDao.findRolePerms(roleId);
  }

  @Override
  public boolean checkPermCodeUnique(String permCode) {
    PermEntity permEntity = permDao.findByCode(permCode);
    if (permEntity == null) {
      return false;
    }
    return true;
  }

  @Override
  public void close() throws Exception {
    CDI.current().destroy(this);
  }

}
